BIOST 561: function design

Lecture 4

Announcements

Quick notes about HW1

An example about mutate

From https://www.rstudio.com/wp-content/uploads/2015/02/data-wrangling-cheatsheet.pdf

To see an example, recall our COVID dataset.

library(medicaldata)
covid_testing
## # A tibble: 15,524 × 17
##    subject_id fake_first_name fake_last_name gender pan_day test_id clinic_name 
##         <dbl> <chr>           <chr>          <chr>    <dbl> <chr>   <chr>       
##  1       1412 jhezane         westerling     female       4 covid   inpatient w…
##  2        533 penny           targaryen      female       7 covid   clinical lab
##  3       9134 grunt           rivers         male         7 covid   clinical lab
##  4       8518 melisandre      swyft          female       8 covid   clinical lab
##  5       8967 rolley          karstark       male         8 covid   emergency d…
##  6      11048 megga           karstark       female       8 covid   oncology da…
##  7        663 ithoke          targaryen      male         9 covid   clinical lab
##  8       2158 ravella         frey           female       9 covid   emergency d…
##  9       3794 styr            tyrell         male         9 covid   clinical lab
## 10       4706 wynafryd        seaworth       male         9 covid   clinical lab
## # ℹ 15,514 more rows
## # ℹ 10 more variables: result <chr>, demo_group <chr>, age <dbl>,
## #   drive_thru_ind <dbl>, ct_result <dbl>, orderset <dbl>, payor_group <chr>,
## #   patient_class <chr>, col_rec_tat <dbl>, rec_ver_tat <dbl>
covid_testing %>%
mutate(full_name = paste(fake_first_name, fake_last_name)) %>%
select(fake_first_name, fake_last_name, full_name)

Last bit about tibbles

Now, how exactly are R packages organized?

The DESCRIPTION file

The DESCRIPTION file serves three puposes:

Package: UW561S2025Example
Type: Package
Title: Example package of UW BIOST 561 S2025
Version: 0.1.0
Author: Kevin Lin
Maintainer: Kevin Lin <kzlin@uw.edu>
Description: Describing the EM algorithm for spherical Gaussians.
License: MIT +file LICENSE
Encoding: UTF-8
LazyData: true
RoxygenNote: 7.3.1
URL: https://linnykos.github.io/561_s2025_example/
VignetteBuilder: knitr
Imports:
    MASS,
    mvtnorm
Suggests:
    testthat, knitr, markdown

If we take a look at the glmnet package (to fit a LASSO regression, see https://cran.r-project.org/web/packages/glmnet/index.html):

This file is the literally in https://github.com/cran/glmnet/blob/master/DESCRIPTION.

The R folder

Documenting a function via ROxygen

# run after installing the glmnet package! 
library(glmnet)
?glmnet::rmult

This is the documentation for the glmnet::rmult function.

If you look at what the lines of code starting with the #' characters in https://github.com/cran/glmnet/blob/master/R/rmult.R (i.e., lines 1 through 11), you’ll notice it looks strikingly similar to the documentation.

#' Generate multinomial samples from a probability matrix
#'
#' Generate multinomial samples
#'
#' Simple function that calls the \code{rmultinom} function. It generates a class label
#' for each row of its input matrix of class probabilities.
#'
#' @param p matrix of probabilities, with number of columns the number of classes
#' @return a vector of class memberships
#' @author Trevor Hastie \cr Maintainer: Trevor Hastie <hastie@@stanford.edu>
#' @export rmult

This is because R somehow knew to convert these lines of code into the documentation! This is what ROxygen does – You write the documentation “in-line” with the code, and then R Studio does something magical (which we’ll discuss in a bit) that “converts” this text into a documentation.

What does @export mean?

#' Clean-up all NAs in a matrix
#'
#' Replace all the NAs in each column with the median value of non-NAs
#' (or all 0's if all values are NAs).
#'
#' @param mat a numeric matrix
#'
#' @return a numeric matrix without NAs
#' @export
cleanup_na_matrix <- function(mat){
stopifnot(is.matrix(mat), all(is.numeric(mat)))

n <- nrow(mat)
p <- ncol(mat)
mat <- sapply(1:p, function(j){
.cleanup_vector(mat[,j])
})

return(mat)
}

.cleanup_na_vector <- function(vec){
n <- length(vec)
idx <- which(is.na(vec))
if(length(idx) == n) vec <- rep(0, n)
if(length(idx) > 0){
vec[idx] <- stats::median(vec, na.rm = TRUE)
}

return(vec)
}

The impact of exporting functions

So that’s what’s in the man folder (short for “manual”)

So… how do I use this ROxygen thing myself?

The pipeline (which we will talk in more detail later in course) is:

  1. Write your function in a .R file under the R folder in your package

  1. When your text editor cursor (i.e., where you type) is inside your function, navigate to “Code” -> “Insert Roxygen skeleton”

  1. R Studio will auto-populate some simple things into your text editor above your function

  1. Then, you can write all the fields that you want.

Creating all the files you’re looking for

After you do all this hard to write all these R functions and Roxygen documentation, how do all these other files get auto-generated?

Let’s use our UW561S2024Example package as an example. (This is the same as UW561S2025Example package, except from 2024.)

Let’s say our current R package looks like this:

Then, you can run devtools::check() in the R console (not in your R markdown file). Make sure you’re in your R project for this R package.

Afterwards, you’ll see all these new files inside your folder that we were expecting:

See more with ?devtools::check to see how to you can change how the function works.

Checking and installing a custom R package

devtools::check() actually does a lot of things (which we’ll talk more about later in the course).

After you’ve run devtools::check(), you can run devtools::load_all() to locally load all your functions in your R folder. (Alternatively, if you weren’t in your R project, you would need to run devtools::install() and then load your package via library() as if it were any other package.)

Followup: The NAMESPACE file

The NAMESPACE file is an important file for R packages, but you will not need to create it yourself.

For example, if you look at the NAMESPACE for the glmnet function, it looks like this (from https://github.com/cran/glmnet/blob/master/NAMESPACE):

As you can see, it says # Generated by roxygen2: do not edit by hand. It is automatically generated when you run devtools::document().

Followup: The .Rbuildignore file and .gitignore file

So… Why do my vignettes not appear in the vignettes folder?

Before we go on break …

Group exercise (split into 2 groups)

Suppose I’m given this matrix that encodes a graph:

set.seed(10)
adj_mat <- matrix(stats::rbinom(n = 100,
                                size = 1,
                                prob = 0.25),
                  nrow = 10,
                  ncol = 10)
adj_mat <- adj_mat + t(adj_mat) #Symmeterize
adj_mat[adj_mat > 0] <- 1
diag(adj_mat) <- 0 #Remove self-edges
adj_mat
##       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
##  [1,]    0    0    1    0    0    0    0    0    1     0
##  [2,]    0    0    0    0    0    1    0    0    0     1
##  [3,]    1    0    0    0    0    0    1    0    1     0
##  [4,]    0    0    0    0    0    0    1    1    0     0
##  [5,]    0    0    0    0    0    0    1    0    0     1
##  [6,]    0    1    0    0    0    0    1    0    0     1
##  [7,]    0    0    1    1    1    1    0    0    1     0
##  [8,]    0    0    0    1    0    0    0    0    0     0
##  [9,]    1    0    1    0    0    0    1    0    0     0
## [10,]    0    1    0    0    1    1    0    0    0     0

Write a script where, given this specific graph:

  • Determine if the graph is connected (i.e., there is a way to go from any node to any other node)?

(By the way, this graph looks like this):

g <- igraph::graph_from_adjacency_matrix(adj_mat,
                                         mode = "undirected")
plot(g)

Now onto how to design R functions

Now that you have a high-level sense of how R packages works, the bigger question is: “How should I write my functions?”

In-class question: What is the benefit of having a style guide?

Summary of guiding principles

Default values

Default values, as the name suggests, are when an argument has a default value when it is not explicitly defined the function call.

# A very simple (unnecessary) function that computes the quantile of a vector
demo_function <- function(x, quantile_value){
  stats::quantile(x, probs = quantile_value)
}
# Again, try to avoid having variables the same as function names
# (That is why I named the variable "quantile_value", not "quantile")

set.seed(0)
x <- stats::rnorm(100)
demo_function(x, quantile_value = 0.9)
##      90% 
## 1.239882

If we knew that most of the time, we would be computing the 90% quantile, then we could set quantile=0.9 directly.

demo_function <- function(x, quantile_value = 0.9){
  stats::quantile(x, probs = quantile_value)
}

set.seed(0)
x <- stats::rnorm(100)
# no need to define the quantile!
demo_function(x) 
##      90% 
## 1.239882
# we still have the flexibility to change this
demo_function(x, quantile_value = 0.5) 
##         50% 
## -0.03296148

Many functions you use day-to-day have many default values

This one is from stats::lm. We see that the argument method, model, qr, etc. (arguments you probably didn’t even realize, or probably never set yourself) have default arguments!

See https://www.rdocumentation.org/packages/stats/versions/3.6.2/topics/lm

WARNING: There’s a price for convenience

Default values are great when you’re done with your project and am ready to release your codebase to the world (since it means other users need to worry about less arguments).

HOWEVER: While default values are convenient (i.e., you set them once, and never need to worry about them again), SET DEFAULT VALUES TO YOUR FUNCTIONS SPARINGLY while you’re working on your research project.

By avoiding using default values during your project’s development, you force yourself to declare all your arguments upfront, so you never are uncertain which arguments use which values.

(Yes: Your code will look uglier if you avoid setting default values. But also, yes, future-you will be grateful for this.)

The output of functions

A few tips on how to return things in your functions:

Unlike C++ and Java, functions in R do not check the class of the outputs.

“public” vs. “private” functions

You might’ve noticed that I sometimes put a . before the function argument. For example, the .cleanup_na_vector() function:

cleanup_na_matrix <- function(mat){
stopifnot(is.matrix(mat), all(is.numeric(mat)))

n <- nrow(mat)
p <- ncol(mat)
mat <- sapply(1:p, function(j){
.cleanup_vector(mat[,j])
})

return(mat)
}

.cleanup_na_vector <- function(vec){
n <- length(vec)
idx <- which(is.na(vec))
if(length(idx) == n) vec <- rep(0, n)
if(length(idx) > 0){
vec[idx] <- stats::median(vec, na.rm = TRUE)
}

return(vec)
}

Curiously enough, Google’s style-guide also mentioned this:

What on earth is this about?

When I run these lines of code, notice the .cleanup_na_vector() is “hidden” from the environment.

When you put a . in front of your function, you are making the intent of the function clear – this function is NOT meant for anyone else except the author (you) to use.

This is “weaker” (i.e., not as explicit) as not exporting your function via @export using Roxygen, but it has a similar effect.

Prefixing the functions inside your function

Another odd thing you might’ve realized is that I use :: in my functions:

.cleanup_na_vector <- function(vec){
n <- length(vec)
idx <- which(is.na(vec))
if(length(idx) == n) vec <- rep(0, n)
if(length(idx) > 0){
vec[idx] <- stats::median(vec, na.rm = TRUE)
}

return(vec)
}

Curiously, Google’s Style-guide also states this and explains why:

Why should you prefix your function-calls with the package it’s from?

Reason 1: So someone else can understand your code. Take this example of my code from 2019:

clique_selection <- function(...){ # some arguments
d <- vcount(g)
adj <- as.matrix(as_adjacency_matrix(g))

clique_list <- lapply(maximal.cliques(g), function(x){
sort(as.numeric(x))
})
if(any(!is.na(target_idx))) {
clique_list <- prune_clique(adj,
clique_list,
target_idx,
threshold)
}
if(length(clique_list) == 1) return(list(1:ncol(adj)))
largest_clique <- list(sort(clique_list[[
which.max(sapply(clique_list, length))
]]))

len <- length(clique_list)
queue <- initialize_queue(len)
hash_history <- hash()
hash_children <- initialize_children(clique_list)
hash_unique <- initialize_unique(clique_list, d)

# ... some more lines later on
}

If you saw this code for the first time, there are so many new functions, you might be very confused – which functions did Kevin write, and which functions did he use a package for?

Instead, if it were written as:

clique_selection <- function(...){ # some arguments
d <- igraph::vcount(g)
adj <- as.matrix(igraph::as_adjacency_matrix(g))

clique_list <- lapply(igraph::maximal.cliques(g), function(x){
sort(as.numeric(x))
})
if(any(!is.na(target_idx))) {
clique_list <- prune_clique(adj,
clique_list,
target_idx,
threshold)
}
if(length(clique_list) == 1) return(list(1:ncol(adj)))
largest_clique <- list(sort(clique_list[[
which.max(sapply(clique_list, length))
]]))

len <- length(clique_list)
queue <- initialize_queue(len)
hash_history <- hash::hash()
hash_children <- initialize_children(clique_list)
hash_unique <- initialize_unique(clique_list, d)

# ... some more lines later on
}

Then you can be like, “Aha, I see, so I only need to worry about understanding all the function without a ::, since those are the functions that Kevin must’ve defined somewhere.”

Reason 2: There might be a function name that used in multiple packages, and you want to make sure R uses the correct one.

Consider the example of compute the difference between extreme left-tail areas of a standard Gaussian. (We’re 29 standard deviations away from the mean!! These values are tiny)

pnorm(-29) - pnorm(-29.1)
## [1] 3.110291e-185
# this equivalent to:
stats::pnorm(-29) - stats::pnorm(-29.1)
## [1] 3.110291e-185
# since the `pnorm` function is by default in the `stats` package

However, what if we specifically wanted to use the pnorm function in Rmpfr package (a package used for extreme scientific computing)?

library(Rmpfr)
## Warning: package 'Rmpfr' was built under R version 4.3.3
## Warning: package 'gmp' was built under R version 4.3.3
x1 <- Rmpfr::mpfr(29.1, precBits = 1000)
x2 <- Rmpfr::mpfr(29.0, precBits = 1000)

Rmpfr::pnorm(x1) - Rmpfr::pnorm(x2)
## 1 'mpfr' number of precision  1000   bits 
## [1] 3.11029098704306832744474436058609456171386153827002784398827445343783468205809241614918290447477493715533670078051481511208922767421005799290297947391098211100990256393690562080315015040624013303799996522960755454471672441838758748773990322372780718078380051229122166634344894041531378421329369285889169e-185
# (I wouldn't try running this code yourself. 
# This code is just to showcase!)

The order of your arguments

compute_snns <- function(input_obj,
                         latent_k,
                         num_neigh,
                         bool_cosine = T,
                         bool_intersect = T,
                         min_deg = 1,
                         tol = 1e-4,
                         verbose = 0){
  # ...
}

There’s a sense that the input object (input_obj) is the “most important” argument, hence it’s first.

This personal heuristic is great for exported functions (i.e., “public” functions you expect other people to use), since it’ll make their life easier.

HOWEVER: For “private” functions (i.e., functions you name with a . in front, that you don’t expect other people to use), it is very easy to make a coding error by forgetting what the literal order of arguments is.

A very common way to introduce bugs is that you passed in the arguments in wrong order!!

Here are the rules I follow (whenever I remember) for “private” functions:

.tiltedCCA_common_score <- function(averaging_mat,
                                    cca_res, 
                                    discretization_gridsize,
                                    enforce_boundary,
                                    fix_tilt_perc, 
                                    snn_bool_cosine,
                                    snn_bool_intersect,
                                    snn_k,
                                    snn_min_deg,
                                    snn_num_neigh,
                                    svd_1, 
                                    svd_2, 
                                    target_dimred,
                                    verbose = 0){
  # ...
}
# some code in some other function
res <- .tiltedCCA_common_score(averaging_mat = averaging_mat,
                               cca_res = cca_res, 
                               discretization_gridsize = discretization_gridsize,
                               enforce_boundary = enforce_boundary,
                               fix_tilt_perc = fix_tilt_perc, 
                               snn_bool_cosine = snn_bool_cosine,
                               snn_bool_intersect = snn_bool_intersect,
                               snn_k = snn_k,
                               snn_min_deg = snn_min_deg,
                               snn_num_neigh = snn_num_neigh,
                               svd_1 = svd_1, 
                               svd_2 = svd_2, 
                               target_dimred = target_dimred,
                               verbose = verbose)

Yes, again, this makes your code uglier. But, also yes, your future-self will thank current-you for this.

How should I name my variables or function?

On this aspect, I don’t have much strong opinions on. Here is what I do:

Aside: How does the stats::lm() function work?

One last thing before we end today’s lecture.

ctl <- c(4.17,5.58,5.18,6.11,4.50,4.61,5.17,4.53,5.33,5.14)
trt <- c(4.81,4.17,4.41,3.59,5.87,3.83,6.03,4.89,4.32,4.69)
group <- gl(2, 10, 20, labels = c("Ctl","Trt"))
weight <- c(ctl, trt)
lm_D9 <- stats::lm(weight ~ group)

lm_D9 is a list.

is.list(lm_D9)
## [1] TRUE
names(lm_D9)
##  [1] "coefficients"  "residuals"     "effects"       "rank"         
##  [5] "fitted.values" "assign"        "qr"            "df.residual"  
##  [9] "contrasts"     "xlevels"       "call"          "terms"        
## [13] "model"

Somehow, when we run print() on lm_D9, we can some informative text.

print(lm_D9) # equivalent to simplying typing: lm_D9
## 
## Call:
## stats::lm(formula = weight ~ group)
## 
## Coefficients:
## (Intercept)     groupTrt  
##       5.032       -0.371

But this isn’t the normal behavior of a list…

example_list <- list(a = 1:10, b = "asdf")
print(example_list)
## $a
##  [1]  1  2  3  4  5  6  7  8  9 10
## 
## $b
## [1] "asdf"

Somehow, when we run plot() on lm_D9, we can some informative plots.

plot(lm_D9) 

But this isn’t the normal behavior of a list…

plot(example_list)
## Error in xy.coords(x, y, xlabel, ylabel, log): 'x' is a list, but does not have components 'x' and 'y'

Somehow, when we run summary() on lm_D9, we can an informative summary.

summary(lm_D9) 
## 
## Call:
## stats::lm(formula = weight ~ group)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -1.0710 -0.4938  0.0685  0.2462  1.3690 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   5.0320     0.2202  22.850 9.55e-15 ***
## groupTrt     -0.3710     0.3114  -1.191    0.249    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.6964 on 18 degrees of freedom
## Multiple R-squared:  0.07308,    Adjusted R-squared:  0.02158 
## F-statistic: 1.419 on 1 and 18 DF,  p-value: 0.249

But this isn’t the normal behavior of a list…

summary(example_list)
##   Length Class  Mode     
## a 10     -none- numeric  
## b  1     -none- character

What is going on? How does this happen?

A brief aside about S3 generics in R

The result of stats::lm() actually are of class lm.

class(lm_D9)
## [1] "lm"

It’s actually more than just a list!

Furthermore, when you call print(lm_D9), you’re actually using the function print.lm(). See https://github.com/SurajGupta/r-source/blob/master/src/library/stats/R/lm.R#L248

But… how did we actually end up using print.lm() without explicitly calling it?

methods("print")
##   [1] print.acf*                                          
##   [2] print.activeConcordance*                            
##   [3] print.AES*                                          
##   [4] print.all_vars*                                     
##   [5] print.anova*                                        
##   [6] print.any_vars*                                     
##   [7] print.aov*                                          
##   [8] print.aovlist*                                      
##   [9] print.ar*                                           
##  [10] print.Arima*                                        
##  [11] print.arima0*                                       
##  [12] print.AsIs                                          
##  [13] print.aspell*                                       
##  [14] print.aspell_inspect_context*                       
##  [15] print.bibentry*                                     
##  [16] print.Bibtex*                                       
##  [17] print.bigq*                                         
##  [18] print.bigz*                                         
##  [19] print.browseVignettes*                              
##  [20] print.bslib_breakpoints*                            
##  [21] print.bslib_fragment*                               
##  [22] print.bslib_navbar_options*                         
##  [23] print.bslib_page*                                   
##  [24] print.bslib_showcase_layout*                        
##  [25] print.bslib_value_box_theme*                        
##  [26] print.by                                            
##  [27] print.cachem*                                       
##  [28] print.changedFiles*                                 
##  [29] print.check_bogus_return*                           
##  [30] print.check_code_usage_in_package*                  
##  [31] print.check_compiled_code*                          
##  [32] print.check_demo_index*                             
##  [33] print.check_depdef*                                 
##  [34] print.check_details*                                
##  [35] print.check_details_changes*                        
##  [36] print.check_doi_db*                                 
##  [37] print.check_dotInternal*                            
##  [38] print.check_make_vars*                              
##  [39] print.check_nonAPI_calls*                           
##  [40] print.check_package_code_assign_to_globalenv*       
##  [41] print.check_package_code_attach*                    
##  [42] print.check_package_code_data_into_globalenv*       
##  [43] print.check_package_code_startup_functions*         
##  [44] print.check_package_code_syntax*                    
##  [45] print.check_package_code_unload_functions*          
##  [46] print.check_package_compact_datasets*               
##  [47] print.check_package_CRAN_incoming*                  
##  [48] print.check_package_datalist*                       
##  [49] print.check_package_datasets*                       
##  [50] print.check_package_depends*                        
##  [51] print.check_package_description*                    
##  [52] print.check_package_description_encoding*           
##  [53] print.check_package_license*                        
##  [54] print.check_packages_in_dir*                        
##  [55] print.check_packages_used*                          
##  [56] print.check_po_files*                               
##  [57] print.check_pragmas*                                
##  [58] print.check_Rd_line_widths*                         
##  [59] print.check_Rd_metadata*                            
##  [60] print.check_Rd_xrefs*                               
##  [61] print.check_RegSym_calls*                           
##  [62] print.check_S3_methods_needing_delayed_registration*
##  [63] print.check_so_symbols*                             
##  [64] print.check_T_and_F*                                
##  [65] print.check_url_db*                                 
##  [66] print.check_vignette_index*                         
##  [67] print.checkDocFiles*                                
##  [68] print.checkDocStyle*                                
##  [69] print.checkFF*                                      
##  [70] print.checkRd*                                      
##  [71] print.checkRdContents*                              
##  [72] print.checkReplaceFuns*                             
##  [73] print.checkS3methods*                               
##  [74] print.checkTnF*                                     
##  [75] print.checkVignettes*                               
##  [76] print.citation*                                     
##  [77] print.cli_ansi_html_style*                          
##  [78] print.cli_ansi_string*                              
##  [79] print.cli_ansi_style*                               
##  [80] print.cli_boxx*                                     
##  [81] print.cli_diff_chr*                                 
##  [82] print.cli_doc*                                      
##  [83] print.cli_progress_demo*                            
##  [84] print.cli_rule*                                     
##  [85] print.cli_sitrep*                                   
##  [86] print.cli_spark*                                    
##  [87] print.cli_spinner*                                  
##  [88] print.cli_tree*                                     
##  [89] print.codoc*                                        
##  [90] print.codocClasses*                                 
##  [91] print.codocData*                                    
##  [92] print.cohesiveBlocks*                               
##  [93] print.col_spec*                                     
##  [94] print.collector*                                    
##  [95] print.colorConverter*                               
##  [96] print.communities*                                  
##  [97] print.compactPDF*                                   
##  [98] print.condition                                     
##  [99] print.connection                                    
## [100] print.CRAN_package_reverse_dependencies_and_views*  
## [101] print.css*                                          
## [102] print.data.frame                                    
## [103] print.Date                                          
## [104] print.date_names*                                   
## [105] print.default                                       
## [106] print.dendrogram*                                   
## [107] print.density*                                      
## [108] print.difftime                                      
## [109] print.dist*                                         
## [110] print.Dlist                                         
## [111] print.DLLInfo                                       
## [112] print.DLLInfoList                                   
## [113] print.DLLRegisteredRoutines                         
## [114] print.document_context*                             
## [115] print.document_position*                            
## [116] print.document_range*                               
## [117] print.document_selection*                           
## [118] print.dplyr_join_by*                                
## [119] print.dplyr_sel_vars*                               
## [120] print.dummy_coef*                                   
## [121] print.dummy_coef_list*                              
## [122] print.ecdf*                                         
## [123] print.eigen                                         
## [124] print.element*                                      
## [125] print.evaluate_evaluation*                          
## [126] print.factanal*                                     
## [127] print.factor                                        
## [128] print.family*                                       
## [129] print.fileSnapshot*                                 
## [130] print.findLineNumResult*                            
## [131] print.flatGridListing*                              
## [132] print.formula*                                      
## [133] print.fseq*                                         
## [134] print.ftable*                                       
## [135] print.fun_list*                                     
## [136] print.function                                      
## [137] print.getAnywhere*                                  
## [138] print.ggplot*                                       
## [139] print.ggplot2_bins*                                 
## [140] print.ggproto*                                      
## [141] print.ggproto_method*                               
## [142] print.gList*                                        
## [143] print.glm*                                          
## [144] print.glue*                                         
## [145] print.gpar*                                         
## [146] print.GridCoords*                                   
## [147] print.GridGrobCoords*                               
## [148] print.GridGTreeCoords*                              
## [149] print.grob*                                         
## [150] print.gtable*                                       
## [151] print.hashtab*                                      
## [152] print.hcl_palettes*                                 
## [153] print.hclust*                                       
## [154] print.help_files_with_topic*                        
## [155] print.hexmode                                       
## [156] print.hms*                                          
## [157] print.HoltWinters*                                  
## [158] print.hsearch*                                      
## [159] print.hsearch_db*                                   
## [160] print.htest*                                        
## [161] print.html*                                         
## [162] print.html_dependency*                              
## [163] print.htmltools.selector*                           
## [164] print.htmltools.selector.list*                      
## [165] print.igraph*                                       
## [166] print.igraph_layout_modifier*                       
## [167] print.igraph_layout_spec*                           
## [168] print.igraph.es*                                    
## [169] print.igraph.vs*                                    
## [170] print.igraphHRG*                                    
## [171] print.igraphHRGConsensus*                           
## [172] print.infl*                                         
## [173] print.integrate*                                    
## [174] print.integrateR*                                   
## [175] print.isoreg*                                       
## [176] print.json*                                         
## [177] print.key_missing*                                  
## [178] print.kmeans*                                       
## [179] print.knitr_kable*                                  
## [180] print.last_dplyr_warnings*                          
## [181] print.Latex*                                        
## [182] print.LaTeX*                                        
## [183] print.libraryIQR                                    
## [184] print.lifecycle_warnings*                           
## [185] print.listof                                        
## [186] print.lm*                                           
## [187] print.loadings*                                     
## [188] print.locale*                                       
## [189] print.loess*                                        
## [190] print.logLik*                                       
## [191] print.ls_str*                                       
## [192] print.medpolish*                                    
## [193] print.membership*                                   
## [194] print.MethodsFunction*                              
## [195] print.mpfr*                                         
## [196] print.mpfr1*                                        
## [197] print.mpfrArray*                                    
## [198] print.mtable*                                       
## [199] print.NativeRoutineList                             
## [200] print.Ncharacter*                                   
## [201] print.news_db*                                      
## [202] print.nls*                                          
## [203] print.noquote                                       
## [204] print.numeric_version                               
## [205] print.object_size*                                  
## [206] print.octmode                                       
## [207] print.packageDescription*                           
## [208] print.packageInfo                                   
## [209] print.packageIQR*                                   
## [210] print.packageStatus*                                
## [211] print.paged_df*                                     
## [212] print.pairwise.htest*                               
## [213] print.path*                                         
## [214] print.person*                                       
## [215] print.pillar*                                       
## [216] print.pillar_1e*                                    
## [217] print.pillar_colonnade*                             
## [218] print.pillar_ornament*                              
## [219] print.pillar_shaft*                                 
## [220] print.pillar_squeezed_colonnade*                    
## [221] print.pillar_tbl_format_setup*                      
## [222] print.pillar_vctr*                                  
## [223] print.pillar_vctr_attr*                             
## [224] print.POSIXct                                       
## [225] print.POSIXlt                                       
## [226] print.power.htest*                                  
## [227] print.ppr*                                          
## [228] print.prcomp*                                       
## [229] print.princomp*                                     
## [230] print.proc_time                                     
## [231] print.purrr_function_compose*                       
## [232] print.purrr_function_partial*                       
## [233] print.purrr_rate_backoff*                           
## [234] print.purrr_rate_delay*                             
## [235] print.quosure*                                      
## [236] print.quosures*                                     
## [237] print.R6*                                           
## [238] print.R6ClassGenerator*                             
## [239] print.raster*                                       
## [240] print.Rconcordance*                                 
## [241] print.Rd*                                           
## [242] print.recordedplot*                                 
## [243] print.rel*                                          
## [244] print.restart                                       
## [245] print.RGBcolorConverter*                            
## [246] print.RGlyphFont*                                   
## [247] print.rlang_box_done*                               
## [248] print.rlang_box_splice*                             
## [249] print.rlang_data_pronoun*                           
## [250] print.rlang_dict*                                   
## [251] print.rlang_dyn_array*                              
## [252] print.rlang_envs*                                   
## [253] print.rlang_error*                                  
## [254] print.rlang_fake_data_pronoun*                      
## [255] print.rlang_lambda_function*                        
## [256] print.rlang_message*                                
## [257] print.rlang_trace*                                  
## [258] print.rlang_warning*                                
## [259] print.rlang_zap*                                    
## [260] print.rlang:::list_of_conditions*                   
## [261] print.rle                                           
## [262] print.rlib_bytes*                                   
## [263] print.rlib_error_3_0*                               
## [264] print.rlib_trace_3_0*                               
## [265] print.roman*                                        
## [266] print.sass*                                         
## [267] print.sass_bundle*                                  
## [268] print.sass_layer*                                   
## [269] print.scalar*                                       
## [270] print.sessionInfo*                                  
## [271] print.shiny.tag*                                    
## [272] print.shiny.tag.env*                                
## [273] print.shiny.tag.list*                               
## [274] print.shiny.tag.query*                              
## [275] print.simple.list                                   
## [276] print.smooth.spline*                                
## [277] print.socket*                                       
## [278] print.src*                                          
## [279] print.srcfile                                       
## [280] print.srcref                                        
## [281] print.stepfun*                                      
## [282] print.stl*                                          
## [283] print.stringr_view*                                 
## [284] print.StructTS*                                     
## [285] print.subdir_tests*                                 
## [286] print.summarize_CRAN_check_status*                  
## [287] print.summary.aov*                                  
## [288] print.summary.aovlist*                              
## [289] print.summary.ecdf*                                 
## [290] print.summary.glm*                                  
## [291] print.summary.lm*                                   
## [292] print.summary.loess*                                
## [293] print.summary.manova*                               
## [294] print.summary.nls*                                  
## [295] print.summary.packageStatus*                        
## [296] print.summary.ppr*                                  
## [297] print.summary.prcomp*                               
## [298] print.summary.princomp*                             
## [299] print.summary.table                                 
## [300] print.summary.warnings                              
## [301] print.summaryDefault                                
## [302] print.summaryMpfr*                                  
## [303] print.table                                         
## [304] print.tables_aov*                                   
## [305] print.tbl*                                          
## [306] print.terms*                                        
## [307] print.theme*                                        
## [308] print.tidyverse_conflicts*                          
## [309] print.tidyverse_logo*                               
## [310] print.transform*                                    
## [311] print.trunc_mat*                                    
## [312] print.ts*                                           
## [313] print.tskernel*                                     
## [314] print.TukeyHSD*                                     
## [315] print.tukeyline*                                    
## [316] print.tukeysmooth*                                  
## [317] print.undoc*                                        
## [318] print.uneval*                                       
## [319] print.unit*                                         
## [320] print.vctrs_bytes*                                  
## [321] print.vctrs_sclr*                                   
## [322] print.vctrs_unspecified*                            
## [323] print.vctrs_vctr*                                   
## [324] print.viewport*                                     
## [325] print.vignette*                                     
## [326] print.warnings                                      
## [327] print.xfun_md_viewable*                             
## [328] print.xfun_raw_string*                              
## [329] print.xfun_record_results*                          
## [330] print.xfun_rename_seq*                              
## [331] print.xfun_strict_list*                             
## [332] print.xgettext*                                     
## [333] print.xngettext*                                    
## [334] print.xtabs*                                        
## see '?methods' for accessing help and source code
print <- function(x, ...) UseMethod("print")

Defining your own class via structure()

create_561 <- function(string){
  structure(string,
            class = "UW561")
}

print.UW561 <- function(x, ...){
  paste("Our custom 561 print statement:", x)
}

result <- create_561("test")
result
## Our custom 561 print statement: test

You can do this with all your fancy results!

If you want to enumerate all the generic functions, see https://stackoverflow.com/questions/15803154/see-which-s3-generic-methods-are-available-in-environment

objs1 <- mget(ls("package:base"), inherits=TRUE)
funs1 <- Filter(is.function, objs1)
genFuns0 <- sapply(
  funs1,
  function(X) {
    if (!is.null(body(X)) & !is.symbol(body(X))) {
      utils::isS3stdGeneric(X)
    }
  },
  simplify=FALSE
)
sort(names(Filter(isTRUE, genFuns0)))
##  [1] "%*%"                      "anyDuplicated"           
##  [3] "as.array"                 "as.Date"                 
##  [5] "as.expression"            "as.function"             
##  [7] "as.list"                  "as.matrix"               
##  [9] "as.null"                  "as.POSIXct"              
## [11] "as.POSIXlt"               "as.single"               
## [13] "as.table"                 "by"                      
## [15] "chol"                     "chooseOpsMethod"         
## [17] "close"                    "conditionCall"           
## [19] "conditionMessage"         "cut"                     
## [21] "date"                     "determinant"             
## [23] "diff"                     "droplevels"              
## [25] "duplicated"               "flush"                   
## [27] "getDLLRegisteredRoutines" "intersect"               
## [29] "is.na<-"                  "isSymmetric"             
## [31] "julian"                   "kappa"                   
## [33] "labels"                   "levels"                  
## [35] "matrix"                   "merge"                   
## [37] "months"                   "mtfrm"                   
## [39] "nameOfClass"              "open"                    
## [41] "plot"                     "pretty"                  
## [43] "print"                    "qr"                      
## [45] "quarters"                 "rev"                     
## [47] "row.names"                "row.names<-"             
## [49] "rowsum"                   "scale"                   
## [51] "seek"                     "seq"                     
## [53] "sequence"                 "setdiff"                 
## [55] "setequal"                 "solve"                   
## [57] "split"                    "split<-"                 
## [59] "subset"                   "toString"                
## [61] "transform"                "truncate"                
## [63] "union"                    "units"                   
## [65] "units<-"                  "weekdays"                
## [67] "with"                     "within"

If you write your own class, you can then write your class’s specific version for any of these!

We’re done with the lecture. A few more things: Piping

More on R packages

With the remaining time…